package com.breeze.media.audio;

import android.content.Context;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.AudioTrack;
import android.media.MediaRecorder;
import android.os.Message;
import android.os.SystemClock;
import android.util.Log;

import com.breeze.media.common.BaseThreadManager;
import com.breeze.media.common.VolumeUtils;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class RecordPlayHelper extends BaseThreadManager {

    private static final String TAG = "RecordPlayHelper";

    private static final int SAMPLE_RATE_HZ = 44100;

    private Context mContext;
    private AudioManager mAudioManager;

    private int mRecordBufferSize;
    private AudioRecord mAudioRecord;
    private BufferedOutputStream mOutputStream = null;

    private int mPlayBufferSize;
    private AudioTrack mAudioTrack;
    private BufferedInputStream mInputStream = null;

    private long mStartTime = 0;
    private long mEndTime = 0;
    private int mReadCount = 0;
    private boolean isRecording = false;
    private boolean isPlaying = false;

    private static final int MSG_START_RECORD = 0x01;
    private static final int MSG_START_PLAY = 0x02;

    public void init(Context context) {
        mContext = context;
        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
        mRecordBufferSize = AudioRecord.getMinBufferSize(SAMPLE_RATE_HZ, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT);
        mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, SAMPLE_RATE_HZ, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT, mRecordBufferSize);

        mPlayBufferSize = AudioTrack.getMinBufferSize(SAMPLE_RATE_HZ, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT);
        mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, SAMPLE_RATE_HZ, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT, mPlayBufferSize, AudioTrack.MODE_STREAM);
    }

    public void end() {
        mAudioRecord.release();
        mAudioRecord = null;
        mAudioTrack.release();
        mAudioTrack = null;
        release();
    }

    public void startRecord() {
        mReadCount = 0;

        isRecording = true;
        mStartTime = SystemClock.elapsedRealtime();
        getThreadHandler().sendEmptyMessage(MSG_START_RECORD);
    }

    public boolean stopRecord() {
        if (!isRecording) {
            return false;
        }
        isRecording = false;
        mEndTime = SystemClock.elapsedRealtime();
        stopAudioRecord();
        return true;
    }

    public void startPlay() {
        isPlaying = true;
        getThreadHandler().sendEmptyMessage(MSG_START_PLAY);
    }

    public synchronized boolean stopPlay() {
        if (!isPlaying) {
            return false;
        }
        isPlaying = false;
        stopPlayRecord();
        return true;
    }

    public long getRecordDuration() {
        return mEndTime - mStartTime;
    }

    public boolean isMicNormal() {
        return mReadCount > 1;
    }

    @Override
    protected void onThreadMessage(Message msg) {
        switch (msg.what) {
            case MSG_START_RECORD:
                startAudioRecord();
                break;
            case MSG_START_PLAY:
                startPlayRecord();
                break;
            default:
                break;
        }
    }

    private void startAudioRecord() {
        setOutputStream();

        mAudioRecord.startRecording();

        byte[] data = new byte[mRecordBufferSize];
        while (AudioRecord.RECORDSTATE_RECORDING == mAudioRecord.getRecordingState()) {
            try {
                int result = mAudioRecord.read(data, 0, mRecordBufferSize);
                mOutputStream.write(data, 0, result);
                double volume = VolumeUtils.calculateVolume(data, 16);
                if (volume > 0.0f || volume < 0.0f) {
                    mReadCount++;
                }
            } catch (Exception e) {
                Log.v(TAG, "startAudioRecord: Exception " + e.getMessage());
                break;
            }
        }

        Log.v(TAG, "mReadCount " + mReadCount);
    }

    private void stopAudioRecord() {
        mAudioRecord.stop();

        if (mOutputStream != null) {
            try {
                mOutputStream.close();
                mOutputStream = null;
            } catch (Exception e) {
                Log.v(TAG, "stopAudioRecord: Exception " + e.getMessage());
            }
        }
    }

    private void startPlayRecord() {
        setInputStream();
        mAudioTrack.play();

        try {
            // TODO: 18-12-21 data没用获取到的min最小字节
            byte[] data = new byte[1024];

            if (mInputStream.available() > 0) {
                while (AudioTrack.PLAYSTATE_PLAYING == mAudioTrack.getPlayState()) {
                    int result = mInputStream.read(data);
                    if (result > 0) {
                        mAudioTrack.write(data, 0, result);
                    } else {
                        Log.v(TAG, "startPlayRecord: end");
                        stopPlay();
                    }
                }
            } else {
                Log.v(TAG, "startPlayRecord: mInputStream.available() < 0");
            }
        } catch (IOException e) {
            Log.v(TAG, "startPlayRecord: Exception " + e.getMessage());
            stopPlay();
        }
    }

    private void stopPlayRecord() {
        mAudioTrack.stop();

        if (mInputStream != null) {
            try {
                mInputStream.close();
                mInputStream = null;
            } catch (Exception e) {
                Log.v(TAG, "stopPlayRecord: Exception " + e.getMessage());
            }
        }
    }

    private void setOutputStream() {
        File file = getFile();
        if (file.exists()) {
            file.delete();
        }
        try {
            file.createNewFile();
        } catch (IOException e) {
            throw new IllegalStateException("create file failed !");
        }
        try {
            OutputStream outputStream = new FileOutputStream(file);
            mOutputStream = new BufferedOutputStream(outputStream);
        } catch (Exception e) {
            Log.v(TAG, "setOutputStream: Exception " + e.getMessage());
        }
    }

    private void setInputStream() {
        File file = getFile();
        if (!file.exists()) {
            Log.v(TAG, "setInputStream: file is not exists");
            return;
        }
        try {
            InputStream inputStream = new FileInputStream(file);
            mInputStream = new BufferedInputStream(inputStream);
        } catch (Exception e) {
            Log.v(TAG, "setInputStream: Exception " + e.getMessage());
        }
    }

    private File mFile;

    private File getFile() {
        if (null == mFile) {
            String fileName = "tmp.pcm";
            File dir = mContext.getFilesDir();
            mFile = new File(dir, fileName);
        }
        return mFile;
    }

    @Override
    protected String getTag() {
        return TAG;
    }
}